import asyncio
import aioredis
import threading
from typing import Any
from datetime import datetime

import settings

try:
    LOOP = asyncio.get_event_loop()
except:
    LOOP = asyncio.new_event_loop()


class AioRedis:
    _instance_lock = threading.Lock()

    def __init__(self, **kwargs):
        self.pool = None
        self.host = kwargs.get("host") or settings.REDIS_HOST
        self.port = kwargs.get("port") or settings.REDIS_PORT
        self.cache_db = kwargs.get("cache_db") or settings.REDIS_CACHE_DB
        self.password = kwargs.get("password") or settings.REDIS_PASSWORD
        self.expired = kwargs.get("expired") or settings.REDIS_CACHE_DB_EXPIRED

    def __new__(cls, *args, **kwargs):
        if not hasattr(AioRedis, "_instance"):
            with AioRedis._instance_lock:
                if not hasattr(AioRedis, "_instance"):
                    AioRedis._instance = super(AioRedis, cls).__new__(cls, *args, **kwargs)
        return AioRedis._instance

    async def redis_connect_pool(self):
        """创建连接池"""
        print("连接Redis:", self.host)
        kwargs = dict(
            loop=LOOP,
            db=self.cache_db,
            encoding="utf-8",
        )
        if self.password:
            kwargs["password"] = self.password
        self.pool = await aioredis.create_pool(address=(self.host, self.port), **kwargs)

    async def set(self, key: str, value: Any, ex: int = None):
        """
        将数据pickle.dumps，转化为二进制bytes数据
        ex_单位为秒
        将数据存储到数据库
        """
        if not self.pool: await self.redis_connect_pool()
        async with self.pool.get() as redis:
            await redis.execute('SET', key, value)
            if ex:
                await redis.execute('EXPIRE', key, ex)

    async def append(self, key: str, value: Any):
        """
        向key列表中追加数据
        """
        if not self.pool: await self.redis_connect_pool()
        async with self.pool.get() as redis:
            await redis.execute('RPUSH', key, value)

    async def get(self, key: str) -> str:
        """
        从数据库根据键（key）获取值
        """
        if not self.pool: await self.redis_connect_pool()
        async with self.pool.get() as redis:
            return await redis.execute("GET", key)

    async def get(self, key: str) -> str:
        """
        从数据库根据键（key）获取值
        """
        if not self.pool: await self.redis_connect_pool()
        async with self.pool.get() as redis:
            return await redis.execute("GET", key)

    async def incr(self, key: str, amount: int = 1):
        """
        从数据库根据键（key）自增
        """
        if not self.pool: await self.redis_connect_pool()
        async with self.pool.get() as redis:
            return await redis.execute("INCRBY", key, amount)

    async def decr(self, key: str, amount: int = 1):
        """
        从数据库根据键（key）获取值
        """
        if not self.pool: await self.redis_connect_pool()
        async with self.pool.get() as redis:
            return await redis.execute("DECRBY", key, amount)

    async def execute(self, operation:str, **kwargs):
        """
        Redis操作
            :param operation:操作符
            :param kwargs:操作传参
        """
        if not self.pool: await self.redis_connect_pool()
        async with self.pool.get() as redis:
            return await redis.execute(operation, **kwargs)

    async def delete(self, *names):
        """
        从数据库根据键（key）删除值
        """
        if not self.pool: await self.redis_connect_pool()
        async with self.pool.get() as redis:
            await redis.execute('DEL', *names)

    async def expire(self, key: str, ex: int = 0):
        """
        从数据库根据键（key）设置过期时间
        """
        if not self.pool: await self.redis_connect_pool()
        if ex and (isinstance(ex, int) or (isinstance(ex, datetime))):
            async with self.pool.get() as redis:
                await redis.execute('EXPIRE', key, ex)

    async def get_time(self, key: str) -> int:
        """获取过期时间"""
        if not self.pool: await self.redis_connect_pool()
        async with self.pool.get() as redis:
            return await redis.execute('TTL', key)

    async def get_more(self, key: str) -> dict:
        """获取值和过期时间"""
        if not self.pool: await self.redis_connect_pool()
        async with self.pool.get() as redis:
            value = await redis.execute('GET', key)
            ttl = await redis.execute('TTL', key)
            return {"value": value, "ttl": ttl}

    async def close(self):
        """关闭连接池"""
        if self.pool:
            await self.pool.wait_closed()


aioRedis = AioRedis()